/*
 * Copyright 2002-2006 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.tigris.jasmincc.framework.implementation;

import java.util.*;

import org.tigris.jasmincc.framework.Coordinator;
import org.tigris.jasmincc.framework.NamingManager;
import org.tigris.jasmincc.framework.namespace.*;
import org.tigris.jasmincc.utils.StringServices;

/**
 * Changes the names of tokens and methods with the instance name.
 * 
 * @author Sam Huang
 * 
 */
public class SimpleNamingManager implements NamingManager {
	protected static List<String> globalDeclarations = new ArrayList<String>();

	protected Coordinator coordinator = null;

	protected NamespaceManager namespaceManager = null;

	protected String simpleGrammarName;

	protected String grammarName;

	protected String instanceName;

	protected Map<String, String> instances = new Hashtable<String, String>();

	protected boolean isPrimaryGrammar = false;

	protected String namespaceName;

	public String getNamespaceName() {
		return namespaceName;
	}

	public void setNamespaceName(String namespaceName) {
		this.namespaceName = namespaceName;
	}

	/**
	 * Constructor for derived classes to override.
	 * 
	 */
	protected SimpleNamingManager() {

	}

	public SimpleNamingManager(Map<String, String> instances) {
		this.instances = instances;
	}

	/**
	 * Add a new declaration, such as the name of methods, etc.
	 */
	public void addDeclaration(String name) {
		globalDeclarations.add(name);
	}

	/**
	 * Add a new instance.
	 * 
	 * @param name
	 *            the declared name of the instance
	 * @param qualifiedName
	 *            the qualified name
	 */
	public void addInstance(String name, String qualifiedName) {
		instances.put(name, qualifiedName);
	}

	public void assertNotEqual(String grammarName, String instanceName)
			throws Exception {
		if (grammarName.equals(instanceName)) {
			throw new Exception("instance name [" + instanceName
					+ "] should be be the same as the grammar name ["
					+ grammarName + "].");
		}
	}

	/**
	 * Build up the qualified name by the current instance name and the
	 * argumented instance name
	 * 
	 * @param grammarName
	 * @param instanceName
	 * @return
	 * @throws RuntimeException
	 */
	public String prefixWithInstanceName(String instanceName)
			throws UnsupportedOperationException {
		String qualifiedName = instanceName;
		if (!this.instanceName.equals("")) {
			qualifiedName = this.instanceName + "_" + instanceName;
		}
		this.instances.put(instanceName, qualifiedName);
		return qualifiedName;
	}

	/**
	 * Change the name of the reference by adding the prefix of the current
	 * instance name.
	 * 
	 */
	public String updateRef(String tk) throws UnsupportedOperationException {
		// a reference
		int dot = tk.lastIndexOf('.');
		if (dot > 0) {
			// it is a reference of another grammar's production
			// get the name of the grammar and submit to the
			// coordinator
			String callerName = tk.substring(0, dot);
			String productionName = tk.substring(dot + 1, tk.length());
			String qualifiedCallerName = getQualifiedName(callerName);
			if (qualifiedCallerName == null) {
				// TODO are we going to support this manner of static access?
				// tk = StringUtility.replace(tk, '.', '_');
				// coordinator.addGrammar(callerName);
				throw new UnsupportedOperationException(
						"cannot find the instance name '" + callerName
								+ "' in grammar '" + grammarName + "'");
			}
			// it is an instance
			qualifiedCallerName = StringServices.replace(qualifiedCallerName,
					'.', '_');
			tk = qualifiedCallerName + "_" + productionName;
		} else {
			// it is a production within the current grammar and its
			// ascednents
			tk = prefixWithInstanceName(tk);
		}
		return tk;
	}

	/**
	 * Check if there is a declaration with the same name.
	 */
	public boolean containsDeclaration(String name) {
		return globalDeclarations.contains(name);
	}

	/**
	 * Get the coordinator
	 * 
	 * @return the coordinator
	 */
	public Coordinator getCoordinator() {
		return coordinator;
	}

	/**
	 * Get the namespace manager
	 * 
	 * @return the namespace manager
	 */
	public NamespaceManager getCorpusManager() {
		return namespaceManager;
	}

	/**
	 * Get the grammar name.
	 */
	public String getGrammarName() {
		return grammarName;
	}

	/**
	 * Get the instance name
	 */
	public String getInstanceName() {
		return instanceName;
	}

	/**
	 * Retrieve all the instances at once.
	 * 
	 * @return the instances
	 */
	public Map<String, String> getInstances() {
		return instances;
	}

	/**
	 * Get the qualified method name by its declared name.
	 * 
	 * @param name
	 *            the declared name
	 * @return null if there is no qualified method name associated with the
	 *         given name
	 */
	protected String getQualifiedMethodName(String name) {
		if (!instanceName.equals("")) {
			return StringServices.replace(instanceName, '.', '_') + "_" + name;
		}
		return null;
	}

	/**
	 * Get the qualified name by declared instance name
	 * 
	 * @param name
	 *            get the qualified name of the declared instance name
	 */
	public String getQualifiedName(String name) {
		return instances.get(name);
	}

	/**
	 * if it is an instance, then get its qualified name, otherwise it should be
	 * a grammar, add it to the waiting list.
	 * 
	 * @param value
	 * @return qualified name or grammar name
	 */
	public String getQualifiedOrGrammarName(String value) {
		if (instances.containsKey(value)) {
			// if it is an instance, then get its qualified name
			value = instances.get(value);
			return instances.get(value);
		}
		return null;
	}

	/**
	 * Check whether there is a qualified associated with the given instance
	 * name.
	 * 
	 * @param name
	 *            instance name
	 */
	public boolean isAnInstance(String name) {
		return instances.containsKey(name);
	}

	/**
	 * Check whether the current grammar is the first grammar among all to be
	 * loaded.
	 * 
	 * @return true if it is.
	 */
	public boolean isPrimaryGrammar() {
		return isPrimaryGrammar;
	}

	/**
	 * Set the coordinator.
	 * 
	 * @param coordinator
	 *            the coordinator
	 */
	public void setCoordinator(Coordinator c) {
		this.coordinator = c;
	}

	/**
	 * Set the grammar name;
	 * 
	 * @param grammarName
	 *            the grammar name
	 */
	public void setGrammarName(String grammarName) {
		this.grammarName = grammarName;
	}

	public void setInstanceName(String gName) {
		this.instanceName = gName;
	}

	/**
	 * Forcefully set up the instances;
	 * 
	 * @param instances
	 *            the instances to set
	 */
	public void setInstances(Hashtable<String, String> instances) {
		this.instances = instances;
	}

	/**
	 * Set the current grammar to be the first grammar or not.
	 * 
	 * @param isPrimaryGrammar
	 *            true if it is.
	 */
	public void setIsPrimaryGrammar(boolean isPrimaryGrammar) {
		this.isPrimaryGrammar = isPrimaryGrammar;
	}

	/**
	 * @param qualifiedCorpusName
	 *            the qualifiedCorpusName to set
	 */
	public void setQualifiedNamespaceName(String namespaceName) {
		this.namespaceName = namespaceName;
	}

	public String getSimpleGrammarName() {
		int dot = this.grammarName.lastIndexOf('.');
		return this.grammarName.substring(dot + 1, this.grammarName.length());
	}

	protected Map<String, List<Map<String, String>>> signatures = new HashMap<String, List<Map<String, String>>>();

	public boolean containsDeclaration(String name, Map<String, String> params) {
		if (!signatures.containsKey(name)) {
			return false;
		}
		List<Map<String, String>> theParams = signatures.get(name);
		for (Map<String, String> p : theParams) {
			if (match(p, params)) {
				return true;
			}
		}
		theParams.add(params);
		signatures.put(name, theParams);
		return false;
	}

	public boolean match(Map<String, String> s1, Map<String, String> s2) {
		String name1, name2;

		for (String key : s1.keySet()) {
			if (!s2.containsKey(key)) {
				return false;
			}
			name1 = s1.get(key);
			name2 = s2.get(key);
			if (!name1.equals(name2)) {
				return false;
			}
		}
		return true;
	}

}
